package com.shape.sdk.collect;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.dsi.ant.plugins.antplus.pcc.AntPlusHeartRatePcc;
import com.dsi.ant.plugins.antplus.pcc.MultiDeviceSearch;
import com.dsi.ant.plugins.antplus.pcc.defines.DeviceState;
import com.dsi.ant.plugins.antplus.pcc.defines.DeviceType;
import com.dsi.ant.plugins.antplus.pcc.defines.EventFlag;
import com.dsi.ant.plugins.antplus.pcc.defines.RequestAccessResult;
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc;
import com.dsi.ant.plugins.antplus.pccbase.AntPluginPcc.IPluginAccessResultReceiver;
import com.dsi.ant.plugins.antplus.pccbase.AntPlusCommonPcc;
import com.dsi.ant.plugins.antplus.pccbase.PccReleaseHandle;

import java.math.BigDecimal;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * Created by ouzhiyin on 09/01/2018.
 */

public class UsbAntCollector implements ICollector {

  private Context context;
  private MultiDeviceSearch hrDeviceScan;
  private Handler mainHandler;
  private Map<String, AntDevice> devicesMap;
  boolean isStoped;

  MultiDeviceSearch.SearchCallbacks searchCallback = new MultiDeviceSearch.SearchCallbacks() {
    @Override
    public void onSearchStarted(MultiDeviceSearch.RssiSupport rssiSupport) {
      Log.d("shapesdk-usb", "rssiSupport: " + rssiSupport);
    }

    @Override
    public void onDeviceFound(com.dsi.ant.plugins.antplus.pccbase.MultiDeviceSearch.MultiDeviceSearchResult multiDeviceSearchResult) {
      Log.d("shapesdk-usb", "deviceFound: " + multiDeviceSearchResult.getAntDeviceNumber()
          + "\nisAlreadyConnected：" + multiDeviceSearchResult.isAlreadyConnected()
          + "\nisUserRecognizedDevice：" + multiDeviceSearchResult.isUserRecognizedDevice()
          + "\nscanResultInternalIdentifier：" + multiDeviceSearchResult.resultID
          + "\nmultiDeviceSearchResult: " + multiDeviceSearchResult.isPreferredDevice()
          + "\ndescribeContents：" + multiDeviceSearchResult.describeContents()
          + "\nantDeviceNumber: " + multiDeviceSearchResult.getAntDeviceNumber());
      if (multiDeviceSearchResult.isAlreadyConnected()) {
        return;
      }
      final int deviceIdentifier = multiDeviceSearchResult.getAntDeviceNumber();
      onStopScan();
      mainHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
          connectDevice(deviceIdentifier);
        }
      }, 2000);
    }

    @Override
    public void onSearchStopped(RequestAccessResult requestAccessResult) {
      Log.d("shapesdk-usb", "requestAccessResult: " + requestAccessResult);
      mainHandler.post(new Runnable() {
        @Override
        public void run() {
          onStopScan();
        }
      });
    }
  };

  MultiDeviceSearch.RssiCallback rssiCallback = new MultiDeviceSearch.RssiCallback() {
    @Override
    public void onRssiUpdate(int i, int i1) {
      Log.d("shapesdk-usb", "onRssiUpdate: " + i + " " + i1);
    }
  };

  public UsbAntCollector(Context context) {
    this.context = context;
    mainHandler = new Handler(Looper.getMainLooper());
    devicesMap = new HashMap<>();
    isStoped = false;
  }

  public void start() {
    isStoped = false;
    startScan();
  }

  public void startScan() {
    if (isStoped) {
      return;
    }
    if (hrDeviceScan == null) {
      hrDeviceScan = new MultiDeviceSearch(this.context, EnumSet.of(DeviceType.HEARTRATE),
          searchCallback, rssiCallback);
    }
  }

  public void stop() {
    isStoped = true;
    if (hrDeviceScan != null) {
      hrDeviceScan.close();
      hrDeviceScan = null;
    }
    for (Map.Entry<String, AntDevice> entry : devicesMap.entrySet()) {
      entry.getValue().onDeviceStop();
    }
    devicesMap.clear();
  }

  @Override
  public DeviceData getHeartRate(String deviceID) {
    AntDevice device = devicesMap.get(deviceID);
    if (device != null) {
      return device.heartDeviceData();
    }
    return null;
  }

  private void reScan() {
    if (isStoped) {
      return;
    }
    onStopScan();
    mainHandler.postDelayed(new Runnable() {
      @Override
      public void run() {
        startScan();
      }
    }, 2000);
  }

  private void onStopScan() {
    if (hrDeviceScan != null) {
      hrDeviceScan.close();
      hrDeviceScan = null;
    }
  }


  private void connectDevice(int identify) {
    AntDevice device  = new AntDevice(this, identify);
    device.connect();
  }

  private void disconnectDecvice(AntDevice device) {
    devicesMap.remove(String.valueOf(device.deviceIdentifier));
    reScan();
  }

  private void onConnected(int identify, AntDevice device) {
    devicesMap.put(String.valueOf(identify), device);
    reScan();
  }

  public Map<String, AntDevice> getAllDevice() {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new IllegalThreadStateException();
    }
    return devicesMap;
  }


  public class AntDevice {
    UsbAntCollector usbAntCollector;
    int deviceIdentifier;
    PccReleaseHandle<AntPlusHeartRatePcc> releaseHandle;
    int count;
    DeviceData deviceData;

    AntDevice(UsbAntCollector usbAntCollector, int deviceIdentifier) {
      this.usbAntCollector = usbAntCollector;
      this.deviceIdentifier = deviceIdentifier;
      deviceData = new DeviceData();
      deviceData.deviceId = String.valueOf(deviceIdentifier);
      count = 0;
    }

    void connect() {
      releaseHandle = AntPlusHeartRatePcc.requestAccess(usbAntCollector.context, deviceIdentifier, 0,
          new IPluginAccessResultReceiver<AntPlusHeartRatePcc>() {
            @Override
            public void onResultReceived(AntPlusHeartRatePcc result,
                                         RequestAccessResult resultCode, DeviceState initialDeviceState) {
              Log.d("shapesdk-usb", "AntPlusHeartRatePcc: " + result
                  + " RequestAccessResult: " + resultCode
                  + " DeviceState: " + initialDeviceState);
              if (result == null) {
                mainHandler.post(new Runnable() {
                  @Override
                  public void run() {
                    onDeviceStop();
                  }
                });
                return;
              }
              mainHandler.post(new Runnable() {
                @Override
                public void run() {
                  onConnected(deviceIdentifier, AntDevice.this);
                }
              });
              result.subscribeHeartRateDataEvent(new AntPlusHeartRatePcc.IHeartRateDataReceiver() {
                @Override
                public void onNewHeartRateData(final long estTimestamp, EnumSet<EventFlag> eventFlags,
                                               final int computedHeartRate, final long heartBeatCount,
                                               final BigDecimal heartBeatEventTime, final AntPlusHeartRatePcc.DataState dataState) {
                  // Mark heart rate with asterisk if zero detected
                  final String textHeartRate = String.valueOf(computedHeartRate)
                      + ((AntPlusHeartRatePcc.DataState.ZERO_DETECTED.equals(dataState)) ? "*" : "");

                  // Mark heart beat count and heart beat event time with asterisk if initial value
                  final String textHeartBeatCount = String.valueOf(heartBeatCount)
                      + ((AntPlusHeartRatePcc.DataState.INITIAL_VALUE.equals(dataState)) ? "*" : "");
                  Log.d("shapesdk-usb", "identify: " + deviceIdentifier
                      + " HeartRate: " + textHeartRate
                      + " HeartBeatCount :" + textHeartBeatCount);
                  deviceData.hr = computedHeartRate;
                }
              });
              result.subscribeRssiEvent(new AntPlusCommonPcc.IRssiReceiver() {
                @Override
                public void onRssiData(long l, EnumSet<EventFlag> enumSet, int i) {
                  deviceData.rssi = i;
                  Log.d("shapesdk-usb", "identify: " + deviceIdentifier
                      + " rssi: " + deviceData.rssi);
                }
              });
            }
          }, new AntPluginPcc.IDeviceStateChangeReceiver() {

            @Override
            public void onDeviceStateChange(DeviceState deviceState) {
              Log.d("shapesdk-usb", "DeviceState: " + deviceState);
              if (deviceState == DeviceState.CLOSED
                  || deviceState == DeviceState.DEAD) {
                mainHandler.post(new Runnable() {
                  @Override
                  public void run() {
                    onDeviceStop();
                  }
                });
              }
            }
          });
    }

    void onDeviceStop() {
      if (releaseHandle != null) {
        releaseHandle.close();
        releaseHandle = null;
      }
      if (usbAntCollector != null) {
        usbAntCollector.disconnectDecvice(this);
        usbAntCollector = null;
      }
    }

    public DeviceData heartDeviceData() {
      return deviceData;
    }
  }

}
